# -*- coding: utf-8 -*-

import sys
from math import sqrt

import numpy as np
import pymysql

# 定义一个执行SQL的函数
conn = pymysql.connect(host='39.101.70.41', port=3306, user='myapp', passwd='cDnS7KZZCjYnSZZ8', db='myapp',
                       charset='utf8')  # 打开数据库连接
cursor = conn.cursor()  # 执行数据库的操作是由cursor完成的,使用cursor()方法获取操作游标
sql = "select * from user_rating"  # 编写sql 查询语句,对应我的表名
cursor.execute(sql)  # 执行sql语句
results = cursor.fetchall()  # 获取查询的所有记录
# print(results)
# print('---------'*20)


users = dict()
for i in results:
    a, b, c = i[0], i[1], round(i[2], 2)
    if a not in users: users[a] = dict()
    for t in range(0,8): users[a][t] = 0
    for t in range(100, 108): users[a][t] = 0
    for t in range(200, 208): users[a][t] = 0
    for t in range(300, 308): users[a][t] = 0
    users[a][b] = c

# print(users)
#
# f = open('/www/wwwroot/Java/ZZR/Python/Recommend.txt', 'w')
# for i in range(results.__len__()):
#     f.write(str(results[i]).strip('()') + '\n')
#
# f.close()
cursor.close()  # 关闭游标
conn.close()  # 关闭数据库连接
#
# filename = "/www/wwwroot/Java/ZZR/Python/Recommend.txt"
# fp = open(filename, "rt", encoding="utf-8")

# ----------------------------------------------------------------------------------------------------------------------

from math import sqrt
import json

# id  ratings  { id: { 'r1' : 5, 'r2' : 4.8, ...} }
# users = {}
#

#
# for i in users_data:
#     if i['id'] not in users:
#         rate_dict = {}
#         for j in range(4):
#             for t in range(8):
#                 rate_dict[j*100+t] = round(i['ratings'][j][t],2)
#         users[i['id']] = rate_dict
#
# print(users)




with open("./data/cuisine.json", 'r', encoding='utf-8') as f:
    cuisine_data = json.load(f)

with open("./data/soup.json", 'r', encoding='utf-8') as f:
    soup_data = json.load(f)

with open("./data/porridge.json", 'r', encoding='utf-8') as f:
    porridge_data = json.load(f)

with open("./data/snack.json", 'r', encoding='utf-8') as f:
    snack_data = json.load(f)

class recommender:
    # users:表示用户信息
    # data: 菜肴数据
    # k: 最相近的邻居个数，这里取2
    # metric: 计算相似度的方法
    # n: 推荐食谱个数，这里取2
    def __init__(self, data, k=2, metric='pearson', n=2):
        self.data = data
        self.k = k
        self.metric = metric
        self.n = n

        # self.username2id = {}
        # self.userid2name = {}
        self.productid2name = {}
        # self.productname2id = {}

    def convertProductID2name(self):
        for i in self.data:
            # print(i)
            if i['id'] is not None:
                self.productid2name[i['id']] = i['name']
                # self.productname2id[self.data[i]['name']] = self.data[i]['id']


    # 皮尔逊相似度计算公式
    def pearson(self, rating1, rating2):
        sum_xy = 0
        sum_x = 0
        sum_y = 0
        sum_x2 = 0
        sum_y2 = 0
        n = 0
        for key in rating1:
            if key in rating2:
                n += 1
                x = rating1[key]
                y = rating2[key]
                sum_xy += x * y
                sum_x += x
                sum_y += y
                sum_x2 += pow(x, 2)
                sum_y2 += pow(y, 2)
        if n == 0:
            return 0

        # 皮尔逊相关系数计算公式
        denominator = sqrt(sum_x2 - pow(sum_x, 2) / n) * sqrt(sum_y2 - pow(sum_y, 2) / n)
        if denominator == 0:
            return 0
        else:
            return (sum_xy - (sum_x * sum_y) / n) / denominator


    def computeNearestNeighbor(self, username):
        distances = []
        for instance in users:
            if instance != username:
                distance = self.pearson(users[username], users[instance])
                distances.append((instance, distance))

        distances.sort(key=lambda food: food[1], reverse=True)
        return distances

    # 推荐算法的主体函数
    def recommend(self, user, data):
        # 将菜肴ID转换成中文名称
        self.convertProductID2name()
        # 定义一个字典，用来存储推荐的菜肴和分数
        recommendations = {}
        # 计算出user与所有其他用户的相似度，返回一个list
        nearest = self.computeNearestNeighbor(user)
        # print nearest

        userRatings = users[user]
        beginRatings = {}   
        for key in userRatings:
            beginRatings[key] = userRatings[key]*0.01
        #         print userRatings
        recommendations = beginRatings
        totalDistance = 0.0
        # 得住最近的k个近邻的总距离
        for i in range(self.k):
            totalDistance += nearest[i][1]
        if totalDistance == 0.0:
            totalDistance = 1.0

        # 将与user最相近的k个人中user没有评过分的推荐给user，并且这里又做了一个分数的计算排名
        for i in range(self.k):

            # 第i个人的与user的相似度，转换到[0,1]之间
            weight = nearest[i][1] / totalDistance

            # 第i个人的name
            name = nearest[i][0]

            # 第i个用户相应的打分
            neighborRatings = users[name]

            for food in neighborRatings:
                if not food in userRatings:
                    if food not in recommendations:
                        recommendations[food] = (neighborRatings[food] * weight)
                    else:
                        recommendations[food] = (recommendations[food] + neighborRatings[food] * weight)

        recommendations = list(recommendations.items())
        cuisine_recommend = recommendations[:8]
        porridge_recommend = recommendations[8:16]
        snack_recommend = recommendations[16:24]
        soup_recommend = recommendations[24:]
        # recommendations = [(self.productid2name[k], v) for (k, v) in recommendations]

        # 做了一个排序
        # recommendations.sort(key=lambda food: food[1], reverse=True)
        cuisine_recommend.sort(key=lambda food: food[1], reverse=True)
        porridge_recommend.sort(key=lambda food: food[1], reverse=True)
        snack_recommend.sort(key=lambda food: food[1], reverse=True)
        soup_recommend.sort(key=lambda food: food[1], reverse=True)

        if data==cuisine_data:
            return cuisine_recommend[:self.n]
        if data==porridge_data:
            return porridge_recommend[:self.n]
        if data==snack_data:
            return snack_recommend[:self.n]
        if data==soup_data:
            return soup_recommend[:self.n]


def adjustrecommend(id, data):
    food_id_list = []
    food_list = []
    r = recommender(data)
    # k, nearuser = r.recommend("%s" % id)
    k = r.recommend(id, data)
    for i in range(len(k)):
        food_id_list.append(k[i][0])
    return food_id_list


def convert_id2name(id_list, data):
    food_list = []
    for j in range(len(id_list)):
        for i in range(len(data)):
            if data[i]['id'] == id_list[j]:
                food_list.append(data[i]['name'])
    return food_list

def main(id):
    cuisine_food_id_list = adjustrecommend(id, cuisine_data)
    porridge_food_id_list = adjustrecommend(id, porridge_data)
    snack_food_id_list = adjustrecommend(id, snack_data)
    soup_food_id_list = adjustrecommend(id, soup_data)

    res = [cuisine_food_id_list[0],cuisine_food_id_list[1],porridge_food_id_list[0],porridge_food_id_list[1],snack_food_id_list[0],snack_food_id_list[1],soup_food_id_list[0],soup_food_id_list[1]]

    return res


# if __name__ == '__main__':
#
#
#     # for i in range(1, len(sys.argv)):
#     #     url = sys.argv[i]
#     #     bookid_list, near_list = adjustrecommend(url)
#     #     print(','.join(bookid_list))
#     id = sys.argv[1]
#     print(main(id))
#
#

# -*- coding: utf-8 -*-
import json
import numpy as np
from collections import defaultdict
from recommend import users, cuisine_data, soup_data, porridge_data, snack_data

# 合并所有菜品数据
all_items = cuisine_data + soup_data + porridge_data + snack_data
item_ids = [item['id'] for item in all_items]

# 模拟测试集：随机选择20%评分作为验证数据
test_set = {}
for user in users:
    rated_items = [item for item, score in users[user].items() if score > 0]
    test_size = max(1, int(len(rated_items) * 0.2))
    test_set[user] = set(np.random.choice(rated_items, test_size, replace=False))


# 评估指标计算类
class RecommenderEvaluator:
    def __init__(self, recommend_func):
        self.recommended_items = defaultdict(set)
        self.all_items = set(item['id'] for item in all_items)
        self.recommend_func = recommend_func

    def generate_recommendations(self, user, n=8):
        # 调用原有推荐系统
        res = self.recommend_func(user)
        recommended = res[:n]  # 取前n个推荐
        self.recommended_items[user].update(recommended)
        return recommended

    # 准确率指标
    def precision_recall(self, user, recommendations):
        test_items = test_set.get(user, set())
        if not test_items:
            return 0, 0

        hits = len(set(recommendations) & test_items)
        precision = hits / len(recommendations) if len(recommendations) > 0 else 0
        recall = hits / len(test_items) if len(test_items) > 0 else 0
        return precision, recall

    # 多样性指标（类别分布）
    def diversity(self, recommendations):
        category_dist = {
            'cuisine': 0, 'soup': 0,
            'porridge': 0, 'snack': 0
        }
        for item_id in recommendations:
            if 0 <= item_id < 100:
                category_dist['cuisine'] += 1
            elif 100 <= item_id < 200:
                category_dist['soup'] += 1
            elif 200 <= item_id < 300:
                category_dist['porridge'] += 1
            else:
                category_dist['snack'] += 1
        return category_dist

    # 覆盖率
    def coverage(self):
        all_recommended = set()
        for items in self.recommended_items.values():
            all_recommended.update(items)
        return len(all_recommended) / len(self.all_items)

    # 新颖性（推荐冷门菜品比例）
    def novelty(self, popularity):
        novelty_scores = []
        for user, items in self.recommended_items.items():
            for item in items:
                novelty_scores.append(1 / (popularity[item] + 1))  # 逆流行度
        return np.mean(novelty_scores) if novelty_scores else 0


# 使用示例
if __name__ == '__main__':
    # 初始化评估器
    evaluator = RecommenderEvaluator(main)

    # 计算流行度基线
    item_popularity = defaultdict(int)
    for user in users:
        for item in users[user]:
            if users[user][item] > 0:
                item_popularity[item] += 1

    # 遍历所有用户进行评估
    total_precision = []
    total_recall = []
    category_dist = defaultdict(int)

    for user in list(users.keys())[:100]:  # 抽样100用户
        recommendations = evaluator.generate_recommendations(user)
        prec, rec = evaluator.precision_recall(user, recommendations)
        total_precision.append(prec)
        total_recall.append(rec)

        # 统计类别分布
        dist = evaluator.diversity(recommendations)
        for k in dist: category_dist[k] += dist[k]

    # 输出结果
    print(f"平均精确率: {np.mean(total_precision):.2f}")
    print(f"平均召回率: {np.mean(total_recall):.2f}")
    print("\n类别分布:")
    for cat, count in category_dist.items():
        print(f"{cat}: {count / sum(category_dist.values()):.1%}")

    print(f"\n覆盖率: {evaluator.coverage():.1%}")
    print(f"新颖性得分: {evaluator.novelty(item_popularity):.3f}")

    # 生成可视化数据
    with open('evaluation_results.json', 'w') as f:
        json.dump({
            'precision': np.mean(total_precision),
            'recall': np.mean(total_recall),
            'coverage': evaluator.coverage(),
            'novelty': evaluator.novelty(item_popularity),
            'category_dist': category_dist
        }, f)

    # for i in users:
    #     id = i
    #     cuisine_food_id_list = adjustrecommend(id, cuisine_data)
    #     porridge_food_id_list = adjustrecommend(id, porridge_data)
    #     snack_food_id_list = adjustrecommend(id, snack_data)
    #     soup_food_id_list = adjustrecommend(id, soup_data)
    #
    #     cuisine_list = convert_id2name(cuisine_food_id_list, cuisine_data)
    #     porridge_list = convert_id2name(porridge_food_id_list, porridge_data)
    #     snack_list = convert_id2name(snack_food_id_list, snack_data)
    #     soup_food_list = convert_id2name(soup_food_id_list, soup_data)


        # print(','.join(cuisine_list))
        # # print("-------111-------")
        # print(','.join(porridge_list))
        # # print("-------222-------")
        # print(','.join(snack_list))
        # # print("-------333-------")
        # print(','.join(soup_food_list))
        # # print("-------444-------")
        # print("\n\n\n------------------------------------------------------")

